/*******************************************************************************
this code is protected by the GNU affero GPLv3
author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
*******************************************************************************/
#include <blkid.h>

#include <ulinux/compiler_types.h>
#include <ulinux/types.h>
#include <ulinux/error.h>
#include <ulinux/utils/mem.h>
#include <ulinux/utils/ascii/string/string.h>
#include <ulinux/utils/ascii/string/conv/decimal/decimal.h>
#include <ulinux/sysc.h>

#include "out.h"
#include "ulinux_namespace.h"
#include "globals.h"

static u8 hdr_action_is_add(u8 *hdr)
{
  #define HDR_ACTION_ADD "add@"
  if(memcmp(hdr,HDR_ACTION_ADD,sizeof(HDR_ACTION_ADD)-1)) return 0;
  return 1;
}

static void hdr_skip(u8 **p)
{
  loop if(*(*p)++==0) break;
}
#define var_skip hdr_skip

static u8 key_is_subsystem(u8 *p)
{
  #define KEY_SUBSYSTEM "SUBSYSTEM="
  if(memcmp(p,KEY_SUBSYSTEM,sizeof(KEY_SUBSYSTEM)-1)) return 0;
  return 1;
}

static u8 key_is_devname(u8 *p)
{
  #define KEY_DEVNAME "DEVNAME="
  if(memcmp(p,KEY_DEVNAME,sizeof(KEY_DEVNAME)-1)) return 0;
  return 1;
}

static void move_to_value(u8 **p)
{
  loop if(*(*p)++=='=') break;
}

static u8 value_is_block(u8 *p)
{
  #define VALUE_BLOCK "block"
  if(!*p||memcmp(p,VALUE_BLOCK,sizeof(VALUE_BLOCK)-1)) return 0;
  return 1;
}

static void value_skip(u8 **p)
{
  loop if(*(*p)++==0) break;
}

static void value_str_consume(u8 **p,u8 *d)
{
  loop{
    *d++=**p;
    if(*(*p)++==0) break;
  }
}

#define IS_ROOT     1
#define IS_NOT_ROOT 0
/*the block device is in devtmpfs, use it to probe with libblkid*/
static u8 blk_dev_probe(u8 *dev_name)
{
  /*the right way to do it is to query the limits on the /dev file system*/
  u8 dev_path[ROOT_DEV_PATH_SZ]="/dev/";
  blkid_probe pr;
  u8 r0;
  i r1;
  const char *type;
  const char *uuid;

  r0=IS_NOT_ROOT;

  strcat(dev_path,dev_name);

  OUT(PRE "probing %s...\n",dev_path);
  pr=blkid_new_probe_from_filename((const char *)dev_path);
  if(!pr){
    OUT(PRE "failed (unable to create a blkid probe), skipping\n");
    return IS_NOT_ROOT;
  }

  r1=blkid_do_probe(pr);
  if(r1!=0){
    OUT(PRE "failed (unable to perform the blkid probe), skipping\n");
    goto free_probe;
  }

  r1=blkid_probe_lookup_value(pr,"TYPE",&type,0);
  if(r1!=0){
    OUT(PRE "failed (missing blkid type tag), skipping\n");
    goto free_probe;
  }
  OUT(PRE "  type=%s\n",type);

  r1=blkid_probe_lookup_value(pr,"UUID",&uuid,0);
  if(r1!=0){
    OUT(PRE "failed (missing blkid uuid tag), skipping\n");
    goto free_probe;
  }
  OUT(PRE "  uuid=%s\n",uuid);

  if(strcmp(uuid,root_uuid)==0){
    /*found the root filesystem*/
    OUT(PRE "found root\n");
    strcpy(root_dev_path,dev_path);
    strcpy(root_fs_type,type);
    r0=IS_ROOT;
  }
  
  OUT(PRE "done\n");
free_probe: 
  blkid_free_probe(pr);
  return r0;
}

/*here we are actually looking for block devices*/
u8 uevent_process(u8 *p,i sz)
{
  u8 *p_end;
  u8 dev_name[256];/*usually a devtmpfs which has path components of 256 bytes*/
  u8 r;

  r=ROOT_NOT_FOUND;
  if(!hdr_action_is_add(p)) return r;
  p_end=p+sz;
  hdr_skip(&p);

  memset(dev_name,0,sizeof(dev_name));

  loop{
    if(p>=p_end) break;

    if(key_is_subsystem(p)){
      move_to_value(&p);
      if(!value_is_block(p)) goto exit;
      else value_skip(&p);
    }else if(key_is_devname(p)){
      move_to_value(&p);
      value_str_consume(&p,dev_name);
    }else var_skip(&p);
  }

  /*from here, we are dealing with a block device, may be our root*/
  r=blk_dev_probe(dev_name);

exit:
  return r;
}
